{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "# YAML Place and AutoRoute\n",
    "\n",
    "You have two options for working with gdsfactory:\n",
    "\n",
    "1. **python flow**: you define your layout using python functions (Parametric Cells), and connect them with routing functions.\n",
    "2. **YAML Place and AutoRoute**: you define your Component as Place and Route in YAML. From the netlist you can simulate the Component or generate the layout.\n",
    "\n",
    "\n",
    "The YAML format contains the schematic together with placement information.\n",
    "\n",
    "YAML is a human readable version of JSON that you can use to define placements and routes\n",
    "\n",
    "to define a a YAML Component you need to define:\n",
    "\n",
    "- instances: with each instance setting\n",
    "- placements: with X and Y\n",
    "\n",
    "And optionally:\n",
    "\n",
    "- routes: between instance ports\n",
    "- connections: to connect instance ports to other ports (without routes)\n",
    "- ports: define input and output ports for the top level component.\n",
    "\n",
    "\n",
    "For using the Schematic YAML Place and AutoRoute we highly recommend using [GDSFactory+](https://gdsfactory.com/) for a seamless and efficient design experience. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import Code\n",
    "\n",
    "import gdsfactory as gf\n",
    "\n",
    "filepath = \"yaml_pics/pads.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3",
   "metadata": {},
   "source": [
    "Let us start by defining the `instances` and `placements` section in YAML\n",
    "\n",
    "Then place a `mmi_long` where you can place the `o1` port at `x=20, y=10`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/mmis.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6",
   "metadata": {},
   "source": [
    "## Ports\n",
    "\n",
    "You can expose any ports of any instance to the new Component with a `ports` section in YAML.\n",
    "\n",
    "Now let us expose all the ports from `mmi_long` into the new component.\n",
    "\n",
    "Ports are exposed as `new_port_name: instance_name, port_name`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/ports_demo.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9",
   "metadata": {},
   "source": [
    "You can also define a mirror placement using a port.\n",
    "\n",
    "Try mirroring with other ports `o2`, `o3` or with other numbers. Try using different rotations as well, such as: `90`, `180`, `270`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/mirror_demo.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "11",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12",
   "metadata": {},
   "source": [
    "## Connections\n",
    "\n",
    "You can connect any two instances by defining a `connections` section in the YAML file.\n",
    "\n",
    "It follows the syntax `instance_source,port : instance_destination,port`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/connections_demo.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15",
   "metadata": {},
   "source": [
    "**Relative port placing**\n",
    "\n",
    "You can also place a component with respect to another instance port.\n",
    "\n",
    "Additionally, you can also define an x and y offset with `dx` and `dy`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/relative_port_placing.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18",
   "metadata": {},
   "source": [
    "## Routes\n",
    "\n",
    "You can define routes between two instances by defining a `routes` section in YAML.\n",
    "\n",
    "it follows the syntax:\n",
    "\n",
    "```YAML\n",
    "\n",
    "routes:\n",
    "    route_name:\n",
    "        links:\n",
    "            instance_source,port: instance_destination,port\n",
    "        settings:  # for the route (optional)\n",
    "            waveguide: strip\n",
    "            width: 1.2\n",
    "\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/routes.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "20",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21",
   "metadata": {},
   "source": [
    "### Routes with steps\n",
    "\n",
    "You can define steps on the route as part of the settings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/routes_steps.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24",
   "metadata": {},
   "source": [
    "### Routes with waypoints\n",
    "\n",
    "You can define waypoints along the route as part of the settings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/routes_waypoints.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27",
   "metadata": {},
   "source": [
    "## Instances, placements, connections, ports, routes\n",
    "\n",
    "Let us combine everything you learned so far.\n",
    "\n",
    "You can define the netlist connections of a component by using a netlist in YAML format.\n",
    "\n",
    "Note that you define the connections as `instance_source.port ->\n",
    "instance_destination.port` so the order is important and therefore you can only\n",
    "change the position of the `instance_destination`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28",
   "metadata": {},
   "source": [
    "You can define several routes that will be connected using `gf.routing.route_bundle`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "29",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/routes_mmi.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31",
   "metadata": {},
   "source": [
    "For placement you can define x, y, rotation and mirror:\n",
    "\n",
    "Notice that mirror is defined as `mirror: True` and rotation as `rotation: 90`, mirror is equivalent to `mirror_y`, so if you want to mirror in the x axis you can use `mirror` and rotate it by 180 degrees."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32",
   "metadata": {},
   "outputs": [],
   "source": [
    "mirror = \"\"\"\n",
    "instances:\n",
    "  a:\n",
    "    component: bend_circular\n",
    "\n",
    "placements:\n",
    "  a:\n",
    "    mirror: True\n",
    "\"\"\"\n",
    "gf.read.from_yaml(mirror)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "33",
   "metadata": {},
   "outputs": [],
   "source": [
    "mirror = \"\"\"\n",
    "instances:\n",
    "  a:\n",
    "    component: bend_circular\n",
    "\n",
    "placements:\n",
    "  a:\n",
    "    mirror: True\n",
    "    rotation: 180\n",
    "\"\"\"\n",
    "gf.read.from_yaml(mirror)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "source": [
    "You can also add custom component_factories to `gf.read.from_yaml`:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "35",
   "metadata": {},
   "outputs": [],
   "source": [
    "@gf.cell\n",
    "def pad_new(size=(100, 100), layer=(1, 0)):\n",
    "    c = gf.Component()\n",
    "    compass = c << gf.components.compass(size=size, layer=layer)\n",
    "    c.ports = compass.ports\n",
    "    return c\n",
    "\n",
    "# gf.get_active_pdk(): This retrieves the currently active PDK.\n",
    "# .register_cells(...): This method adds a new component factory to the PDK.\n",
    "# The argument pad_new=pad_new tells the PDK: \"Add a new component to your library, name it 'pad_new', and use the pad_new function to create it.\"\n",
    "gf.get_active_pdk().register_cells(pad_new=pad_new)\n",
    "c = pad_new()\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/new_factories.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "38",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"yaml_pics/routes_custom.pic.yml\"\n",
    "Code(filepath, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "c = gf.read.from_yaml(filepath)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40",
   "metadata": {},
   "source": [
    "Also, you can define route bundles with different settings and specify the route `factory` as a parameter as well as the `settings` for that particular route alias."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41",
   "metadata": {},
   "source": [
    "## Arrays\n",
    "\n",
    "You can also define arrays and connect them to the array ports using the `instance<column,row>,port_name` syntax:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "42",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Connect port o1 of the component instance named b directly to port o2 of the component instance sa<1.1>\n",
    "# Note: sa(straight array) is used here, but the component instance can be named anything.\n",
    "\n",
    "sample_array = \"\"\"\n",
    "instances:\n",
    "  sa:\n",
    "    component: straight\n",
    "    array:\n",
    "        columns: 2\n",
    "        column_pitch: 20\n",
    "        rows: 3\n",
    "        row_pitch: 20\n",
    "\n",
    "  b:\n",
    "    component: bend_euler\n",
    "\n",
    "connections:\n",
    "    b,o1: sa<1.1>,o2\n",
    "\n",
    "routes:\n",
    "    b1:\n",
    "        settings:\n",
    "          cross_section: strip\n",
    "        links:\n",
    "            sa<0.0>,o2: sa<1.0>,o1\n",
    "\n",
    "ports:\n",
    "    o1: b,o2\n",
    "    o2: sa<0.0>,o1\n",
    "\"\"\"\n",
    "\n",
    "gf.read.from_yaml(sample_array)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "43",
   "metadata": {},
   "source": [
    "## Arrays with routes\n",
    "\n",
    "You can also define groups of route ports in a group using the `instance_name,port_base:index_start:index_end` syntax:\n",
    "\n",
    "For example, if you have an instance `a` with `o3` and `o4` ports, you can define a group of ports `a,o:3:4`  to connect the `o3` and `o4` ports of the instance `a` to the same port of another instance `b`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "44",
   "metadata": {},
   "outputs": [],
   "source": [
    "port_array_optical = \"\"\"\n",
    "instances:\n",
    "  a:\n",
    "    component: nxn\n",
    "  b:\n",
    "    component: nxn\n",
    "\n",
    "placements:\n",
    "  b:\n",
    "    x: 50\n",
    "    y: 50\n",
    "    mirror: True \n",
    "    rotation: 180\n",
    "\n",
    "routes:\n",
    "  optical:\n",
    "    settings:\n",
    "        cross_section: strip\n",
    "    links:\n",
    "      a,o:3:4: b,o:3:4\n",
    "\"\"\"\n",
    "\n",
    "gf.read.from_yaml(port_array_optical)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "45",
   "metadata": {},
   "outputs": [],
   "source": [
    "port_array_electrical = \"\"\"\n",
    "instances:\n",
    "  t:\n",
    "    component: pad_array\n",
    "    settings:\n",
    "      port_orientation: 270\n",
    "      columns: 10\n",
    "      auto_rename_ports: True\n",
    "  b:\n",
    "    component: pad_array\n",
    "    settings:\n",
    "      port_orientation: 90\n",
    "      columns: 10\n",
    "      auto_rename_ports: True\n",
    "\n",
    "placements:\n",
    "  t:\n",
    "    x: 500\n",
    "    y: 600\n",
    "\n",
    "routes:\n",
    "  electrical:\n",
    "    settings:\n",
    "      start_straight_length: 150\n",
    "      end_straight_length: 150\n",
    "      cross_section: metal_routing\n",
    "      allow_width_mismatch: True\n",
    "      sort_ports: True\n",
    "    links:\n",
    "      t,e:10:1: b,e:1:10\n",
    "\"\"\"\n",
    "\n",
    "gf.read.from_yaml(port_array_electrical)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46",
   "metadata": {},
   "source": [
    "## Jinja Pcells\n",
    "\n",
    "You use jinja templates in YAML cells to define Pcells."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "47",
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import Code\n",
    "\n",
    "import gdsfactory as gf\n",
    "from gdsfactory.read import cell_from_yaml_template\n",
    "\n",
    "jinja_yaml = \"\"\"\n",
    "default_settings:\n",
    "    length_mmi:\n",
    "      value: 10\n",
    "      description: \"The length of the long MMI\"\n",
    "    width_mmi:\n",
    "      value: 5\n",
    "      description: \"The width of both MMIs\"\n",
    "\n",
    "instances:\n",
    "    mmi_long:\n",
    "      component: mmi1x2\n",
    "      settings:\n",
    "        width_mmi: {{ width_mmi }}\n",
    "        length_mmi: {{ length_mmi }}\n",
    "    mmi_short:\n",
    "      component: mmi1x2\n",
    "      settings:\n",
    "        width_mmi: {{ width_mmi }}\n",
    "        length_mmi: 5\n",
    "connections:\n",
    "    mmi_long,o2: mmi_short,o1\n",
    "\n",
    "ports:\n",
    "    o1: mmi_long,o1\n",
    "    o2: mmi_short,o2\n",
    "    o3: mmi_short,o3\n",
    "\"\"\"\n",
    "pic_filename = \"demo_jinja.pic.yml\"\n",
    "\n",
    "# A string variable named jinja_yaml (which contains a YAML netlist with Jinja2 templating logic) is written to a file named demo_jinja.pic.yml.\n",
    "with open(pic_filename, mode=\"w\") as f:\n",
    "    f.write(jinja_yaml)\n",
    "\n",
    "# The cell_from_yaml_template function reads the .yml file,\n",
    "# processes the Jinja2 template to generate a standard YAML netlist, and then uses that netlist to build a gdsfactory component.\n",
    "pic_cell = cell_from_yaml_template(pic_filename, name=\"demo_jinja\")\n",
    "\n",
    "# The newly created component is added to the active Process Design Kit (PDK) under the name demo_jinja,\n",
    "# making it easily accessible for reuse in other parts of the design.\n",
    "gf.get_active_pdk().register_cells(\n",
    "    demo_jinja=pic_cell\n",
    ")  # Now let us register this cell so we can use it later.\n",
    "Code(filename=pic_filename, language=\"yaml+jinja\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48",
   "metadata": {},
   "source": [
    "You will see that this generated a python function, with a real signature, default arguments, docstring and all!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49",
   "metadata": {},
   "outputs": [],
   "source": [
    "help(pic_cell)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50",
   "metadata": {},
   "source": [
    "You can instantiate this cell without arguments to see the default implementation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "51",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = pic_cell()\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52",
   "metadata": {},
   "source": [
    "Or you can provide arguments explicitly, like a normal cell. Note however that YAML-based cells **only accept keyword arguments**, since yaml dictionaries are inherently unordered."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = pic_cell(length_mmi=100)\n",
    "c.plot()"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "custom_cell_magics": "kql",
   "encoding": "# -*- coding: utf-8 -*-"
  },
  "kernelspec": {
   "display_name": "gdsfactory",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
